﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;

using System.CodeDom;
using System.CodeDom.Compiler;

namespace Weazel.Gezel.Conversions.MiniCCodeProvider
{
	/// <summary>
	/// Use this class to generate C++ headers from a
	/// CodeDom code representation. The corresponding 
	/// code files can be generated using the 
	/// MiniCCodeGenerator class.
	/// </summary>
	public class MiniCHeaderGenerator : CodeGenerator
	{
		/// <summary>
		/// Used to determine if a code line should be 
		/// wrapped.
		/// </summary>
		private const int MaxLineLength = 80;

		public MiniCHeaderGenerator()
		{
		}

		protected override void ContinueOnNewLine(string st)
		{
			throw new NotImplementedException();
		}

		protected override string CreateEscapedIdentifier(string value)
		{
			// just pray this is not a keyword ;)
			return value;
		}

		protected override string CreateValidIdentifier(string value)
		{
			// just pray this is not a keyword ;)
			return value;
		}

		protected override void GenerateArgumentReferenceExpression(CodeArgumentReferenceExpression e)
		{
			OutputIdentifier(e.ParameterName);
		}

		protected override void GenerateArrayCreateExpression(CodeArrayCreateExpression e)
		{
			Output.WriteLine("/* array create not implemented */");
			//throw new NotImplementedException();
		}

		protected override void GenerateArrayIndexerExpression(CodeArrayIndexerExpression e)
		{
			GenerateExpression(e.TargetObject);
			Output.Write("[");
			bool first = true;
			foreach (CodeExpression exp in e.Indices)
			{
				if (first)
				{
					first = false;
				}
				else
				{
					Output.Write(", ");
				}
				GenerateExpression(exp);
			}
			Output.Write("]");
		}

		protected override void GenerateAssignStatement(CodeAssignStatement e)
		{
			//GenerateExpression(e.Left);
			//Output.Write(" = ");
			//GenerateExpression(e.Right);
			//if (!forLoopHack)
			//{
			//  Output.WriteLine(";");
			//}
		}

		protected override void GenerateAttachEventStatement(CodeAttachEventStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateAttributeDeclarationsEnd(CodeAttributeDeclarationCollection attributes)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateAttributeDeclarationsStart(CodeAttributeDeclarationCollection attributes)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateBaseReferenceExpression(CodeBaseReferenceExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateBinaryOperatorExpression(CodeBinaryOperatorExpression e)
		{
			GenerateExpression(e.Left);

			switch (e.Operator)
			{
				case CodeBinaryOperatorType.Add:
					Output.Write(" + ");
					break;
				case CodeBinaryOperatorType.Assign:
					Output.Write(" = ");
					break;
				case CodeBinaryOperatorType.BitwiseAnd:
					Output.Write(" & ");
					break;
				case CodeBinaryOperatorType.BitwiseOr:
					Output.Write(" | ");
					break;
				case CodeBinaryOperatorType.BooleanAnd:
					Output.Write(" && ");
					break;
				case CodeBinaryOperatorType.BooleanOr:
					Output.Write(" || ");
					break;
				case CodeBinaryOperatorType.Divide:
					Output.Write(" / ");
					break;
				case CodeBinaryOperatorType.GreaterThan:
					Output.Write(" > ");
					break;
				case CodeBinaryOperatorType.GreaterThanOrEqual:
					Output.Write(" >= ");
					break;
				case CodeBinaryOperatorType.IdentityEquality:
					Output.Write(" == ");
					break;
				case CodeBinaryOperatorType.IdentityInequality:
					Output.Write(" != ");
					break;
				case CodeBinaryOperatorType.LessThan:
					Output.Write(" < ");
					break;
				case CodeBinaryOperatorType.LessThanOrEqual:
					Output.Write(" <= ");
					break;
				case CodeBinaryOperatorType.Modulus:
					Output.Write(" % ");
					break;
				case CodeBinaryOperatorType.Multiply:
					Output.Write(" * ");
					break;
				case CodeBinaryOperatorType.Subtract:
					Output.Write(" - ");
					break;
				case CodeBinaryOperatorType.ValueEquality:
					throw new NotSupportedException();
				default:
					throw new ApplicationException();
			}

			GenerateExpression(e.Right);
		}

		protected override void GenerateCastExpression(CodeCastExpression e)
		{
			throw new NotImplementedException();
		}

		public override void GenerateCodeFromMember(CodeTypeMember member, System.IO.TextWriter writer, CodeGeneratorOptions options)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateComment(CodeComment e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateCommentStatement(CodeCommentStatement e)
		{
			Output.WriteLine("/* {0} */", e.Comment.Text);
		}

		protected override void GenerateCommentStatements(CodeCommentStatementCollection e)
		{
			foreach (CodeCommentStatement statement in e)
				GenerateCommentStatement(statement);
		}

		protected override void GenerateCompileUnit(CodeCompileUnit e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateCompileUnitEnd(CodeCompileUnit e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateCompileUnitStart(CodeCompileUnit e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateConditionStatement(CodeConditionStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateConstructor(CodeConstructor e, CodeTypeDeclaration c)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDecimalValue(decimal d)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDefaultValueExpression(CodeDefaultValueExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDelegateCreateExpression(CodeDelegateCreateExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDelegateInvokeExpression(CodeDelegateInvokeExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDirectionExpression(CodeDirectionExpression e)
		{
			switch (e.Direction)
			{
				case FieldDirection.Ref:
					Output.Write("&(");
					break;
				case FieldDirection.In:
				case FieldDirection.Out:
				default:
					throw new NotSupportedException();
			}
			GenerateExpression(e.Expression);

			switch (e.Direction)
			{
				case FieldDirection.Ref:
					Output.Write(")");
					break;
				case FieldDirection.In:
				case FieldDirection.Out:
				default:
					throw new NotSupportedException();
			}
		}

		protected override void GenerateDirectives(CodeDirectiveCollection directives)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateDoubleValue(double d)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateEntryPointMethod(CodeEntryPointMethod e, CodeTypeDeclaration c)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateEvent(CodeMemberEvent e, CodeTypeDeclaration c)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateEventReferenceExpression(CodeEventReferenceExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateExpressionStatement(CodeExpressionStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateField(CodeMemberField e)
		{			
			string fieldName = e.Name;
			string fieldType = e.Type.BaseType;
			bool isArray = e.Type.ArrayRank > 0;
			bool isStatic = (e.Attributes & MemberAttributes.Static) != 0;

			if (e.Type.BaseType != "Int32" && e.Type.BaseType != "ofstream")
			{
				fieldType += "*";
			}

			if (e.Type.ArrayRank > 0)
			{
				Debug.Assert(e.Type.ArrayRank == 1);
				fieldType += "*";
			}

			Output.WriteLine(
				"{0}{1} {2};",
				isStatic ? "static " : String.Empty,
				fieldType, 
				e.Name
			);
		}

		protected override void GenerateFieldReferenceExpression(CodeFieldReferenceExpression e)
		{
			if (e.TargetObject != null && e.TargetObject is CodeTypeReferenceExpression)
			{
				GenerateExpression(e.TargetObject);
				Output.Write("::");
			} 
			else if(e.TargetObject != null) 
			{
				GenerateExpression(e.TargetObject);
				Output.Write("->");
			}
			OutputIdentifier(e.FieldName);
		}

		protected override void GenerateGotoStatement(CodeGotoStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateIndexerExpression(CodeIndexerExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateIterationStatement(CodeIterationStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateLabeledStatement(CodeLabeledStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateLinePragmaEnd(CodeLinePragma e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateLinePragmaStart(CodeLinePragma e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateMethod(CodeMemberMethod e, CodeTypeDeclaration c)
		{
			string methodName = e.Name;
			string returnType = e.ReturnType.BaseType;
			bool isStatic = (e.Attributes & MemberAttributes.Static) != 0;
			bool isArray = e.ReturnType.ArrayRank > 0;

			string parameterList = "void";

			if (e.Parameters.Count > 0)
			{
				Debug.Assert(e.Parameters[0].Name == "outport");
				parameterList = "IOutport** outport";
			}

			if (returnType == "System.Void")
				returnType = "void";
			else
			{
				returnType += "*";
			}

			if (isArray)
			{
				Debug.Assert(e.ReturnType.ArrayRank == 1);
				returnType += "*";
			}

			Output.WriteLine("{0}{1} {2}({3});", isStatic ? "static " : string.Empty, returnType, methodName, parameterList);
		
		}

		protected override void GenerateMethodInvokeExpression(CodeMethodInvokeExpression e)
		{
			GenerateMethodReferenceExpression(e.Method);
			Output.Write("(");
			OutputExpressionList(e.Parameters);
			Output.Write(")");

		}

		protected override void GenerateMethodReferenceExpression(CodeMethodReferenceExpression e)
		{
			if (e.TargetObject != null)
			{
				if (e.TargetObject is CodeBinaryOperatorExpression)
				{
					Output.Write("(");
					GenerateExpression(e.TargetObject);
					Output.Write(")");
				}
				else
				{
					GenerateExpression(e.TargetObject);
				}
				Output.Write("->"); 
			}
			OutputIdentifier(e.MethodName);
		}

		protected override void GenerateMethodReturnStatement(CodeMethodReturnStatement e)
		{
			Output.Write("return");
			if (e.Expression != null) {
				Output.Write(" ");
				GenerateExpression(e.Expression);
			}
			Output.WriteLine(";");
		}

		protected override void GenerateNamespace(CodeNamespace e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateNamespaceEnd(CodeNamespace e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateNamespaceImport(CodeNamespaceImport e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateNamespaceStart(CodeNamespace e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateObjectCreateExpression(CodeObjectCreateExpression e)
		{
			Output.Write("new ");
			OutputType(e.CreateType);
			Output.Write("(");
			OutputExpressionList(e.Parameters);
			Output.Write(")");	
		}

		protected override void GenerateParameterDeclarationExpression(CodeParameterDeclarationExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GeneratePrimitiveExpression(CodePrimitiveExpression e)
		{
			if (e.Value is char)
			{
				GeneratePrimitiveChar((char)e.Value);
			}
			else
			{
				base.GeneratePrimitiveExpression(e);
			}
		}

		private void GeneratePrimitiveChar(char c)
		{
			Output.Write('\'');
			switch (c)
			{
				case '\r':
					Output.Write("\\r");
					break;
				case '\t':
					Output.Write("\\t");
					break;
				case '\"':
					Output.Write("\\\"");
					break;
				case '\'':
					Output.Write("\\\'");
					break;
				case '\\':
					Output.Write("\\\\");
					break;
				case '\0':
					Output.Write("\\0");
					break;
				case '\n':
					Output.Write("\\n");
					break;
				default:
					Output.Write(c);
					break;
			}
			Output.Write('\'');
		}

		protected override void GenerateProperty(CodeMemberProperty e, CodeTypeDeclaration c)
		{

			if (e.Name == "Color")
				Output.WriteLine("Color* Color;");
			else
				throw new NotSupportedException();

		}

		protected override void GeneratePropertyReferenceExpression(CodePropertyReferenceExpression e)
		{
			// properties in C gets translated to plain variables, without encapsulation
			Output.Write(e.PropertyName);
			//throw new NotImplementedException();
		}

		protected override void GeneratePropertySetValueReferenceExpression(CodePropertySetValueReferenceExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateRemoveEventStatement(CodeRemoveEventStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateSingleFloatValue(float s)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateSnippetCompileUnit(CodeSnippetCompileUnit e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateSnippetExpression(CodeSnippetExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateSnippetMember(CodeSnippetTypeMember e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateSnippetStatement(CodeSnippetStatement e)
		{
			Output.WriteLine(e.Value);
		}

		protected override void GenerateThisReferenceExpression(CodeThisReferenceExpression e)
		{
			Output.Write("this");
		}

		protected override void GenerateThrowExceptionStatement(CodeThrowExceptionStatement e)
		{
			Output.WriteLine("/* throw exception... */");
			
		}

		protected override void GenerateTryCatchFinallyStatement(CodeTryCatchFinallyStatement e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateTypeConstructor(CodeTypeConstructor e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateTypeEnd(CodeTypeDeclaration e)
		{
			Indent--;
			Output.WriteLine("};");
		}

		protected override void GenerateTypeOfExpression(CodeTypeOfExpression e)
		{
			throw new NotImplementedException();
		}

		protected override void GenerateTypeReferenceExpression(CodeTypeReferenceExpression e)
		{
				Output.Write(e.Type.BaseType);
		}

		protected override void GenerateTypeStart(CodeTypeDeclaration e)
		{
			String interfaces = string.Empty;
			if(e.BaseTypes.Count > 0) {
				Debug.Assert(e.BaseTypes.Count == 1);
				interfaces = string.Format(" : public {0}", e.BaseTypes[0].BaseType);
			}
			Output.WriteLine("class {0}{1} ", e.Name, interfaces);
			Output.WriteLine("{");
			Output.WriteLine("public:");
			Indent++;
		}

		protected override void GenerateVariableDeclarationStatement(CodeVariableDeclarationStatement e)
		{
			//OutputTypeNamePair(e.Type, e.Name);
			//if (e.InitExpression != null)
			//{
			//  Output.Write(" = ");
			//  GenerateExpression(e.InitExpression);
			//}
			//if (!forLoopHack)
			//{
			//  Output.WriteLine(";");
			//}
		}

		protected override void GenerateVariableReferenceExpression(CodeVariableReferenceExpression e)
		{
			OutputIdentifier(e.VariableName);
		}

		private string GetBaseTypeOutput(string baseType)
		{
			string s = CreateEscapedIdentifier(baseType);
			if (s.Length == 0)
			{
				s = "void";
			}
			else if (string.Compare(s, "System.Int16", true) == 0)
			{
				s = "short";
			}
			else if (string.Compare(s, "System.Int32", true) == 0)
			{
				s = "int";
			}
			else if (string.Compare(s, "System.Int64", true) == 0)
			{
				s = "long";
			}
			else if (string.Compare(s, "System.String", true) == 0)
			{
				s = "string";
			}
			else if (string.Compare(s, "System.Object", true) == 0)
			{
				s = "object";
			}
			else if (string.Compare(s, "System.Boolean", true) == 0)
			{
				s = "bool";
			}
			else if (string.Compare(s, "System.Void", true) == 0)
			{
				s = "void";
			}
			else if (string.Compare(s, "System.Char", true) == 0)
			{
				s = "char";
			}

			else
			{
				// replace + with . for nested classes.
				//
				s = s.Replace('+', '.');
			}
			return s;
		}

		protected override string GetTypeOutput(CodeTypeReference typeRef)
		{
			string s;
			if (typeRef.ArrayElementType != null)
			{
				// Recurse up
				s = GetTypeOutput(typeRef.ArrayElementType);
			}
			else
			{

				s = GetBaseTypeOutput(typeRef.BaseType);
			}
			// Now spit out the array postfix
			if (typeRef.ArrayRank > 0)
			{
				char[] results = new char[typeRef.ArrayRank + 1];
				results[0] = '[';
				results[typeRef.ArrayRank] = ']';
				for (int i = 1; i < typeRef.ArrayRank; i++)
				{
					results[i] = ',';
				}
				s += new string(results);
			}
			return s;
		}

		protected override bool IsValidIdentifier(string value)
		{
			throw new NotImplementedException();
		}

		protected override string NullToken
		{
			get { return "NULL"; }
		}

		protected override void OutputAttributeArgument(CodeAttributeArgument arg)
		{
			throw new NotImplementedException();
		}

		protected override void OutputAttributeDeclarations(CodeAttributeDeclarationCollection attributes)
		{
			throw new NotImplementedException();
		}

		protected override void OutputDirection(FieldDirection dir)
		{
			throw new NotImplementedException();
		}

		protected override void OutputExpressionList(CodeExpressionCollection expressions)
		{
			int expressionCount = expressions.Count;
			if (expressionCount > 0)
			{
				
				for (int i = 0; i < expressionCount - 1; i++)
				{
					GenerateExpression(expressions[i]);
					Output.Write(", ");
				}
				GenerateExpression(expressions[expressionCount - 1]);
			}
		}

		private string QuoteSnippetStringVerbatimStyle(string value)
		{
			StringBuilder b = new StringBuilder(value.Length + 5);

			b.Append("@\"");

			for (int i = 0; i < value.Length; i++)
			{
				if (value[i] == '\"')
					b.Append("\"\"");
				else
					b.Append(value[i]);
			}

			b.Append("\"");

			return b.ToString();
		}

		protected override string QuoteSnippetString(string value)
		{
			// If the string is short, use C style quoting (e.g "\r\n")
			// Also do it if it is too long to fit in one line
			if (value.Length < 256 || value.Length > 1500)
				return QuoteSnippetStringCStyle(value);

			// Otherwise, use 'verbatim' style quoting (e.g. @"foo")
			return QuoteSnippetStringVerbatimStyle(value);
		}


		protected string QuoteSnippetStringCStyle(string value)
		{
			StringBuilder b = new StringBuilder(value.Length + 5);

			b.Append("\"");

			for (int i = 0; i < value.Length; i++)
			{
				switch (value[i])
				{
					case '\r':
						b.Append("\\r");
						break;
					case '\t':
						b.Append("\\t");
						break;
					case '\"':
						b.Append("\\\"");
						break;
					case '\'':
						b.Append("\\\'");
						break;
					case '\\':
						b.Append("\\\\");
						break;
					case '\0':
						b.Append("\\0");
						break;
					case '\n':
						b.Append("\\n");
						break;
					default:
						b.Append(value[i]);
						break;
				}

				if (i > 0 && i % MaxLineLength == 0)
				{
					b.Append("\" +\r\n\"");
				}
			}

			b.Append("\"");

			return b.ToString();
		}


		protected override bool Supports(GeneratorSupport support)
		{
			return true;
			//throw new NotImplementedException();
		}

		protected override void OutputType(CodeTypeReference typeRef)
		{
			Output.Write(GetTypeOutput(typeRef));
		}			}
}
